#include "searule.h"
#include "freefuncs.h"

void SEARule::mutate (void)
{
	if (this->condition.size() == 0)
	{
		mutate_ChangeLabel();
		return;
	}

	int mutationType = rand() % SEAMutationTypes;
	switch ((SEAMutationType) mutationType)
	{
	case removepartialcondition:
		mutate_RemovePartialCondition();
		break;
	case addpartialcondition:
		mutate_AddPartialCondition();
		break;
	case reverseonepartialcondition:
		mutate_ReverseOnePartialCondition();
		break;
	case changeoneneighbor:
		mutate_ChangeOneNeighbor();
		break;
	case changeonehistorystep:
		if (this->maxHistorySteps > 1)
			mutate_ChangeOneHistoryStep();
		else
			mutate();
		break;
	case changeoneNBvalue:
		mutate_ChangeOneNBValue();
		break;
	case changelabel:
		mutate_ChangeLabel();
		break;
	}
}

void SEARule::mutate_RemovePartialCondition(void)
{
	int removeIndex = rand() % condition.size();
	vector<PartialSEACondition>::iterator it = condition.begin() + removeIndex;
	condition.erase(it);
}

void SEARule::mutate_AddPartialCondition(void)
{
	PartialSEACondition toBeAdded(nBRadius, maxHistorySteps);
	toBeAdded.randomize();
	condition.push_back(toBeAdded);

}

void SEARule::mutate_ReverseOnePartialCondition(void)
{
	int mutationLocation = rand() % condition.size();
	condition[mutationLocation].mutateCondition();
}

void SEARule::mutate_ChangeOneNeighbor(void)
{
	int mutationLocation = rand() % condition.size();
	condition[mutationLocation].mutateNBIndex();
}

void SEARule::mutate_ChangeOneHistoryStep(void)
{
	int mutationLocation = rand() % condition.size();
	condition[mutationLocation].mutatehistoryStep();
}

void SEARule::mutate_ChangeOneNBValue(void)
{
	int mutationLocation = rand() % condition.size();
	condition[mutationLocation].mutatenBValue();
}

void SEARule::mutate_ChangeLabel(void)
{
	//label = rand() % CELLSTATES;
	int maxLabelOffset = CELLSTATES - 2;
	int labelOffset = rand() % maxLabelOffset + 1;
	label = rand() % (CELLSTATES-1); //Note: Label can never be 2, or whatever we have for the OUTOFBORDER value.
}

bool SEARule::applies (const PixelHistory& pixel) const
{
	vector<PartialSEACondition>::const_iterator it = condition.begin();
	for (; it != condition.end(); ++it)
		if (!it->applies(pixel))
			return false;
	return true;
}

int SEARule::getLabel(void) const
{
	return label;
}

void SEARule::initiateRandom (void)
{
	if (condition.size() > 0)
	{
		//cerr << "Warning: This SEARule already has some conditions. Cleaning the rule...";
		condition.clear();
	}
	int maxConditions = intPosPow((2*nBRadius+1),2);
	int conditionNum = rand() % maxConditions;
	PartialSEACondition toBeAdded(nBRadius, maxHistorySteps);
	for (int i = 0; i < conditionNum; ++i)
	{
		toBeAdded.randomize();
		condition.push_back(toBeAdded);
	}
	label = rand() % CELLSTATES;
}

SEARule& SEARule::operator= (const SEARule& other)
{
	if (&other == this)
		return *this;
	this->maxHistorySteps = other.maxHistorySteps;
	this->nBRadius = other.nBRadius;
	condition.clear();
	vector<PartialSEACondition>::const_iterator it = other.condition.begin();
	for (; it != other.condition.end(); ++it)
		condition.push_back(*it);
	label = other.label;
	return *this;
}

ostream& operator<< (ostream& stream, const SEARule& searule)
{
	if (searule.condition.size() == 0)
	{
		stream << "Whatever the neighborhood is ";
	}
	else
	{
		stream << "IF ";
		for (int i = 0; i < (int) searule.condition.size() - 1; ++i)
			stream << searule.condition[i] << " AND ";
		stream << searule.condition[(int) searule.condition.size() - 1];
	}
	stream << " THEN label = " << searule.label;
	return stream;
}

SEARule::SEARule(): label(-1)
{}

SEARule::SEARule(const SEARule& other)
{
	*this = other;
}

SEARule::SEARule(int radius, int historySteps, int label):nBRadius(radius),maxHistorySteps(historySteps),label(label)
{}

int SEARule::getConditionNum() const
{
	return condition.size();
}